package com.sunrise.xdoc.view;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;

import com.sunrise.xdoc.dao.IBaseDao;
import com.sunrise.xdoc.entity.AbstractIdEntity;
import com.sunrise.xdoc.helper.DaoHelper;
import com.sunrise.xdoc.utils.SunriseClassUtils;

/**
 * Data model for the table, with sort and filter header.
 */
public abstract class AbstractListDataModel<T extends AbstractIdEntity> extends LazyDataModel<T> {
	private static final long serialVersionUID = 6827448945103889598L;
	private List<T> entities;

	// A map <property name of the entity (can be nested), value to filter
	protected Map<String, Object> filters = new HashMap<>();

	@Autowired
	private DaoHelper daoHelper;

	@Override
	public T getRowData(String rowKey) {
		for (T e : entities) {
			if (Long.valueOf(rowKey).equals(e.getId())) {
				return e;
			}
		}
		return null;
	}

	public void clearFilters() {
		filters.clear();
	}

	@Override
	public Object getRowKey(T entity) {
		return entity.getId();
	}

	@Override
	public List<T> load(int first, int pageSize, String sortField, SortOrder sortOrder,
			Map<String, String> filtersDefault) {
		IBaseDao<T> dao = daoHelper.getDao(getEntityClass());
		DetachedCriteria criteria = dao.getDetachedCriteria();
		if (sortField != null) {
			if (sortOrder == SortOrder.ASCENDING) {
				criteria.addOrder(Order.asc(sortField));
			} else if (sortOrder == SortOrder.DESCENDING) {
				criteria.addOrder(Order.desc(sortField));
			}
		}
		for (String filterProperty : filtersDefault.keySet()) {
			criteria.add(Restrictions.ilike(filterProperty, filtersDefault.get(filterProperty), MatchMode.ANYWHERE));
		}

		filter(criteria, filters);

		this.setRowCount((int) dao.findCountByCriteria(criteria));
		entities = daoHelper.getDao(getEntityClass()).findByCriteria(criteria, first, pageSize);
		return entities;
	}

	/**
	 * The default filter implementation for the property name. Filter as
	 * 'EQUAL' for Number, and ILIKE for String. Support property as path.
	 * 
	 * @param criteria
	 * @param filters
	 */
	protected void filter(DetachedCriteria criteria, Map<String, Object> filters) {
		for (String propertyPath : filters.keySet()) {
			Object searchVal = filters.get(propertyPath);
			if (searchVal != null) {
				String property = propertyPath;
				if (propertyPath.contains(".")) {
					// Not fetching the ending properties => remove it from the
					// pathName
					String[] paths = StringUtils.split(propertyPath, '.');
					for (int i = 0; i < paths.length - 1; i++) {
						criteria.createCriteria(paths[i]);
					}
					propertyPath = paths[paths.length - 1];
				}
				if (searchVal instanceof String) {
					criteria.add(Restrictions.ilike(property, (String) searchVal, MatchMode.ANYWHERE));
				} else {
					criteria.add(Restrictions.eq(property, searchVal));
				}
			}
		}
	};

	@SuppressWarnings("unchecked")
	private Class<T> getEntityClass() {
		return (Class<T>) SunriseClassUtils.getArgumentClass(getClass());
	}

	public Map<String, Object> getFilters() {
		return filters;
	}

	public void setFilters(Map<String, Object> filters) {
		this.filters = filters;
	}
}